﻿#if MONO_CECIL
#else
#define _NET_REFLECTOR
#endif

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Model;

namespace XmlDBAdapter
{
    public class Adapter
    {
        #region Lifecycle

        public Adapter() { }

        #endregion Lifecycle

        #region Properties

        private ActualEntities pContext;
        public ActualEntities Context
        {
            get
            {
                return pContext;
            }
            set
            {
                if (value != pContext)
                    pContext = value;
            }
        }

        #endregion Properties

        #region Methods

#if DEBUG
        static int sFirstTry = 0;
        static int sSecondTry = 0;
        static int sThirdTry = 0;
        static int sFourthTry = 0;
        static int sFifthTry = 0;
        static int sJustFirstTry = 0;
        static int sJustSecondTry = 0;
        static int sJustThirdTry = 0;
        static int sJustFourthTry = 0;
        static int sJustFifthTry = 0;
#endif

        /// <summary>
        /// Parses the XML content of the given FxCop 10.0 report and stores it to the database.
        /// </summary>
        /// <param name="analysisReportPath">Describes the filename and path of the file that contains
        /// the FxCop report</param>
        /// <exception cref="ArgumentNullException">
        /// When <para name="analysisReportPath"/> is null
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// When <para name="context"/> is null, or not set and <see cref="Context"/> is null
        /// </exception>
        /// <exception cref="FileNotFoundException">
        /// When the file with the given path and name described in <para name="analysisReportPath"/>
        /// is not exists
        /// </exception>
        public void ParseToDB(string analysisReportPath, ActualEntities context = null)
        {
            #region ...
            if (analysisReportPath == null)
                throw new ArgumentNullException("analysisReportPath");
            if (File.Exists(analysisReportPath) == false)
                throw new FileNotFoundException("File not found", analysisReportPath);
            if (context == null && this.Context == null)
                throw new ArgumentNullException("context", "If context is not set, Context must be set!");

            if (context != null && this.Context != context)
                this.Context = context;

            List<string> namespaceNames = new List<string>(this.Context.Namespaces.Count());
            foreach (var ns in this.Context.Namespaces)
            {
                if (ns.Types.Count() > 0)
                    namespaceNames.Add(ns.Name);
            }

            XElement root = XElement.Load(analysisReportPath, LoadOptions.SetLineInfo);
            int maxProgress = Count(root);
            int progressCount = 0;
            IEnumerable<XElement> pretargetNamespaces = null;
            if (root.Element("Namespace") != null)
                pretargetNamespaces = from element in root.Element("Namespaces").Elements("Namespace")
                                      select element;
            var targets = from element in root.Element("Targets").Elements("Target")
                          select element;
            var rules = from element in root.Element("Rules").Elements("Rules")
                        select element;

            // pre-target Namespaces
            if (pretargetNamespaces != null)
                foreach (var n in pretargetNamespaces)
                {
                    var ns =
                        ParseNamespaceMessages(assembly: null, storeModuleMessagesOnAssembly: false, module: null,
                            xNamespace: n, xRules: rules,
                            maxProgress: maxProgress,
                            progressCount: ref progressCount);
                }

            // Assemblies
            foreach (var target in targets)
            {
                string assemblyName = Path.GetFileNameWithoutExtension(target.Attribute("Name").Value);
#if _NET_REFLECTOR
                Assembly assembly =
                    this.Context.Assemblies.Where(a => a.Name.Equals(assemblyName)).First();
#elif MONO_CECIL
                Assembly assembly =
                    this.Context.Assemblies.Where(a => a.Name.StartsWith(assemblyName + ",")).First();
#endif
                var modules = from element in target.Element("Modules").Elements("Module")
                              select element;
                bool storeModuleMessagesOnAssembly = (modules.Count() == 1);
                // Modules
                foreach (var mod in modules)
                {
                    string moduleName = target.Attribute("Name").Value.Split('\\').Last();//mod.Attribute("Name").Value;
                    Module module = this.Context.Modules.Where(
                        m => m.AssemblyName.Equals(assembly.Name)
                            && m.Name.Equals(moduleName)).First();
                    // Messages
                    {
                        var msgs = mod.Element("Messages");
                        if (msgs != null)
                        {
                            var messages = from element in msgs.Elements("Message")
                                           select element;
                            progressCount = ParseMessage(messages, rules,
                                maxProgress, progressCount,
                                assembly, module,
                                storeModuleMessagesOnAssembly: storeModuleMessagesOnAssembly);
                        }
                    }
                    // Namespaces
                    if (mod.Element("Namespaces") != null)
                    {
                        var namespaces = from element in mod.Element("Namespaces").Elements("Namespace")
                                         select element;
                        foreach (var n in namespaces)
                        {
                            Namespace ns =
                                ParseNamespaceMessages(assembly, storeModuleMessagesOnAssembly, module,
                                    n, rules, maxProgress, ref progressCount);
                            // Types
                            var types = from element in n.Element("Types").Elements("Type")
                                        select element;
                            foreach (var t in types)
                            {
                                //TODO_REVIEW: kell spec char.kód feloldás?
                                string typeName = null;
                                string[] typeNames =
                                    ResolveNestedTypeSyntax(
                                        ResolveSpecialCharacters(t.Attribute("Name").Value, resolveNested: false));
                                string outerTypeName = null;
                                if (typeNames.Length == 1)
                                {
                                    typeName = typeNames[0];
                                }
                                else if (typeNames.Length > 1)
                                {
                                    typeName = typeNames[typeNames.Length - 1];
                                    outerTypeName = typeNames[typeNames.Length - 2];
                                }
                                else
                                    ;// impossible
#if _NET_REFLECTOR
                            Model.Type type = this.Context.Types.Where(
                                p => p.Name.Equals(typeName) && p.NamespaceName.Equals(ns.Name)
                                    && p.ModuleName.Equals(module.Name)
                                    && p.ModuleAssemblyName.Equals(assembly.Name)).First();
#elif MONO_CECIL
                                Model.Type type = null;
                                if (typeNames.Length == 1)
                                    type = this.Context.Types.Where(
                                        p => p.Name.Equals(typeName) && p.NamespaceName.Equals(ns.Name)
                                            && p.ModuleName.Equals(module.Name)
                                            && p.ModuleAssemblyName.Equals(assembly.Name)).First();
                                else if (typeNames.Length > 1)
                                {
                                    string outerNsSuffix = "";
                                    for (int i = 0; i < typeNames.Length - 2; i++)
                                    {
                                        outerNsSuffix += "." + typeNames[i];
                                    }
                                    type = this.Context.Types.Where(
                                        p => p.Name.Equals(typeName) && p.ModuleName.Equals(module.Name)
                                            && p.ModuleAssemblyName.Equals(assembly.Name)
                                            && p.Outer_TypeName.Equals(outerTypeName)
                                            && p.Outer_TypeNamespaceName.Equals(ns.Name + outerNsSuffix)
                                            && p.Outer_TypeModuleName.Equals(module.Name)
                                            && p.Outer_TypeModuleAssemblyName.Equals(assembly.Name)).First();
                                }
#endif
                                // Messages
                                {
                                    var msgs = t.Element("Messages");
                                    if (msgs != null)
                                    {
                                        var messages = from element in msgs.Elements("Message")
                                                       select element;
                                        progressCount = ParseMessage(messages, rules,
                                            maxProgress, progressCount,
                                            assembly, module, ns, type,
                                            storeModuleMessagesOnAssembly: storeModuleMessagesOnAssembly);
                                    }
                                }
            #endregion ...
                                if (t.Element("Members") != null)
                                {
                                    // Members
                                    var members = from element in t.Element("Members").Elements("Member")
                                                  select element;
                                    foreach (var m in members)
                                    {
                                        if (ParseMember(namespaceNames, assembly, storeModuleMessagesOnAssembly,
                                            module, ns, typeName, type, m, rules, maxProgress, ref progressCount) == false)
                                        {
                                            var msgs = m.Element("Messages");
                                            if (msgs != null)
                                            {
                                                var messages = from element in msgs.Elements("Message")
                                                               select element;
                                                progressCount = ParseMessage(messages, rules,
                                                    maxProgress, progressCount,
                                                    assembly, module, ns, type,
                                                    storeModuleMessagesOnAssembly: storeModuleMessagesOnAssembly);
                                                Trace.TraceInformation("Messages of {0} are stored to the declaring type {1}",
                                                    ResolveSpecialCharacters(m.Attribute("Name").Value.TrimStart('#'), resolveNested: true),
                                                    typeName);
                                            }
                                        }
                                    } // each member
                                }
                            } // each type
                        } // each namespace
                    }
                } // each module
            } // each target
#if DEBUG
            Debug.WriteLine(
                "[Adapter] 1st: " + sJustFirstTry.ToString() + "/" + sFirstTry.ToString()
                + sJustSecondTry.ToString() + "/" + " 2nd: " + sSecondTry.ToString()
                + sJustThirdTry.ToString() + "/" + " 3rd: " + sThirdTry.ToString()
                + sJustFourthTry.ToString() + "/" + " 4th: " + sFourthTry.ToString()
                + sJustFifthTry.ToString() + "/" + " 5th: " + sFifthTry.ToString());
#endif
        }

        private int Count(XElement root)
        {
            return (int)(double)root.XPathEvaluate("count(//Message)");
        }

        private Namespace ParseNamespaceMessages(Assembly assembly, bool storeModuleMessagesOnAssembly, Module module,
            XElement xNamespace, IEnumerable<XElement> xRules, int maxProgress, ref int progressCount)
        {
            string namespaceName = xNamespace.Attribute("Name").Value;
            Namespace ns = this.Context.Namespaces.Where(
            p => p.Name.Equals(namespaceName)).First();
            // Messages
            {
                var msgs = xNamespace.Element("Messages");
                if (msgs != null)
                {
                    var messages = from element in msgs.Elements("Message")
                                   select element;
                    progressCount = ParseMessage(messages, xRules, maxProgress, progressCount,
                        assembly, module, ns,
                        storeModuleMessagesOnAssembly: storeModuleMessagesOnAssembly);
                }
            }
            return ns;
        }

        delegate bool ComparerDelegate(Member m, string s);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="namespaceNames">List of all used namespaces</param>
        /// <param name="assembly">Containing assembly</param>
        /// <param name="storeModuleMessagesOnAssembly">Determines wether the FxCop messages on a member
        /// will be stored also as message on the containing assembly, or not</param>
        /// <param name="module">Containing module</param>
        /// <param name="ns">Containing namespace</param>
        /// <param name="preprocessedTypeName">Preprocessed typename from FxCop output</param>
        /// <param name="type">Containing type</param>
        /// <param name="memberX">Part of the FxCop output about the member</param>
        /// <returns>Value indicating the success of the method</returns>
        private bool ParseMember(List<string> namespaceNames, Assembly assembly,
            bool storeModuleMessagesOnAssembly, Module module, Namespace ns, string preprocessedTypeName,
            Model.Type type, XElement memberX, IEnumerable<XElement> xRules,
            int maxProgress, ref int progressCount)
        {
            Namespace properNamespace =
                this.Context.GetNamespace(
                    (type.OuterType != null),
                    type.OuterType,
                    type.OuterType != null ? type.OuterType.Namespace : type.Namespace,
                    false);
            string pureMemberName =
                ResolveSpecialCharacters(memberX.Attribute("Name").Value.TrimStart('#'), resolveNested: true);
            string resolvedGetterIndexer, resolvedSetterIndexer;
            string[] memberNames;
            if (TryResolveIndexer(pureMemberName,
                out resolvedGetterIndexer, out resolvedSetterIndexer) == true)
                memberNames = new string[2] { resolvedGetterIndexer, resolvedSetterIndexer };
            else
                memberNames = new string[1] { pureMemberName };

            bool foundMember = false;
            int i = 0;
            foreach (var memberN in memberNames)
            {
                #region Tries
                string[] memberNames2 = new string[memberNames.Length * 2];
                for (int j = 0; j < memberNames.Length; j++)
                {
                    memberNames2[j] = preprocessedTypeName + "." + memberN;
                    memberNames2[j + memberNames.Length] = memberN;
                }
                foreach (string memberName in memberNames2)
                {
                    string varMemberName = memberName;
                    string memberNameWithoutNamespaces =
                        EliminateNamespaces(varMemberName, namespaceNames, false);
                    string memberNameWithoutNamespacesWithSpaces =
                            memberNameWithoutNamespaces.Replace(",", ", ");
                    string pureMemberNameWithSpaces = memberN.Replace(",", ", ");

                    IQueryable<Member> query = null;
#if DEBUG
                    bool justFirstTry = true, justSecondTry = true, justThirdTry = true,
                        justFourthTry = true, justFifthTry = true;
                    sFirstTry++;
#endif
                    Member member = null;
                    if (memberN.Contains('!') == false)
                    {
                        varMemberName = ResolveMemberOfGeneric(varMemberName);
                        query = this.Context.Members.Where(
                            p => p.Name.StartsWith(varMemberName) && p.TypeName.Equals(type.Name)
                                && p.TypeNamespaceName.Equals(properNamespace.Name)
                                && p.TypeModuleName.Equals(module.Name)
                                && p.TypeModuleAssemblyName.Equals(assembly.Name));
                        if (query.Count() == 0)
                        {
                            Debug.WriteLine("[Adapter] " + varMemberName + " not found");
                            memberNameWithoutNamespaces = ResolveMemberOfGeneric(memberNameWithoutNamespaces);
                            query = this.Context.Members.Where(
                                p => p.Name.StartsWith(memberNameWithoutNamespaces)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
#if DEBUG
                            sSecondTry++;
                            justFirstTry = false;
#endif
                        }
                        if (query.Count() == 0 && memberNameWithoutNamespaces.Count(chr => chr == '.') == 1)
                        {
                            string[] typeNameAndMemberName =
                                memberNameWithoutNamespaces.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                            if (typeNameAndMemberName[0].Equals(type.Name) == false)// The message is about an interface member
                            {
                                string resolvedMemberName = type.Name + "." + typeNameAndMemberName.Last();
                                query = this.Context.Members.Where(
                                p => p.Name.StartsWith(resolvedMemberName)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
                            }
                        }
                        if (query.Count() == 0)
                        {
                            Debug.WriteLine("[Adapter] " + memberNameWithoutNamespaces + " not found");
                            memberNameWithoutNamespacesWithSpaces =
                                ResolveMemberOfGeneric(memberNameWithoutNamespacesWithSpaces);
                            query = this.Context.Members.Where(
                                p => p.Name.StartsWith(memberNameWithoutNamespacesWithSpaces)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
#if DEBUG
                            sThirdTry++;
                            justSecondTry = false;
#endif
                        }
                        if (query.Count() == 0 && memberNameWithoutNamespacesWithSpaces.Count(chr => chr == '.') == 1)
                        {
                            string[] typeNameAndMemberName =
                                memberNameWithoutNamespacesWithSpaces.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                            if (typeNameAndMemberName[0].Equals(type.Name) == false)// The message is about an interface member
                            {
                                string resolvedMemberName = type.Name + "." + typeNameAndMemberName.Last();
                                query = this.Context.Members.Where(
                                p => p.Name.StartsWith(resolvedMemberName)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
                            }
                        }
                        if (query.Count() == 0) // case of properties (not get_pr or set_pr, just pr)
                        {
                            Debug.WriteLine("[Adapter] " + memberNameWithoutNamespacesWithSpaces + " not found");
                            string resolvedMemberN = ResolveMemberOfGeneric(memberN);
                            query = this.Context.Members.Where(
                                p => p.Name.StartsWith(resolvedMemberN)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
#if DEBUG
                            sFourthTry++;
                            justThirdTry = false;
#endif
                        }
                        if (query.Count() == 0)
                        {
                            Debug.WriteLine("[Adapter] " + memberN + " not found");
                            pureMemberNameWithSpaces = ResolveMemberOfGeneric(pureMemberNameWithSpaces);
                            query = this.Context.Members.Where(
                                p => p.Name.StartsWith(pureMemberNameWithSpaces)
                                    && p.TypeName.Equals(type.Name)
                                    && p.TypeNamespaceName.Equals(properNamespace.Name)
                                    && p.TypeModuleName.Equals(module.Name)
                                    && p.TypeModuleAssemblyName.Equals(assembly.Name));
#if DEBUG
                            sFifthTry++;
#endif
                        }
                        try
                        {
                            member = query.First();
                        }
                        catch (InvalidOperationException) { }
                        catch (NullReferenceException) { }
                    }
                    else
                    {
                        //DB: member:    GenericType<T,S>..ctor(T, S) : Void
                        //    type:      GenericType`2
                        //FxCop: member: #.ctor(!0,!1)
                        //       type:   GenericType`2
                        //
                        //ComparerDelegate cd = new ComparerDelegate(AreTheSame);
                        query = this.Context.Members.Where(
                            p => p.TypeName.Equals(type.Name));
                        foreach (var item in query)
                        {
                            if (AreTheSame(item, memberNameWithoutNamespaces) == true)
                            {
                                member = item;
                                break;
                            }
                        }
                    }
                    //Debug.Assert(query.Count() > 0);

                    if (member != null)
                    {
                        // Messages
                        {
                            var msgs = memberX.Element("Messages");
                            if (msgs != null)
                            {
                                var messages = from element in msgs.Elements("Message")
                                               select element;
                                progressCount = ParseMessage(messages, xRules, maxProgress, progressCount,
                                    assembly, module, properNamespace, type, member,
                                    storeModuleMessagesOnAssembly: storeModuleMessagesOnAssembly);
                            }
                        }
                        foundMember = true;
                    }
#if DEBUG
                    i++;
                    Debug.Assert(
                        (i == memberNames.Length && foundMember == false) == false);
                    if (justFirstTry == true)
                        sJustFirstTry++;
                    if (justSecondTry == true)
                        sJustSecondTry++;
                    if (justThirdTry == true)
                        sJustThirdTry++;
                    if (justFourthTry == true)
                        sJustFourthTry++;
                    if (justFifthTry == true)
                        sJustFifthTry++;
#endif
                }
                #endregion Tries
            } // each memberNames
            if (foundMember == false)
                Trace.TraceError(memberX.ToString().Substring(0, 80) + "... not found!");
            return foundMember;
        }

        private static string ResolveMemberOfGeneric(string memberName)
        {
#if _NET_REFLECTOR
            if (memberName.Contains('`') == false)
                return memberName;

            for (int i = 0; i < 10; i++)
                memberName = memberName.Replace("`" + i.ToString(), "");
            return memberName;
#elif MONO_CECIL
            return memberName;
#endif
        }

#if _NET_REFLECTOR
        private bool AreTheSame(Member p, string memberNameWithoutNamespaces)
        {
            //DB: member:    System.Void AnalysisTestTarget.GenericType`2::.ctor(T,S)   MOMO_CECIL
            //DB: member:    GenericType<T,S>..ctor(T, S) : Void                        _NET_REFLECTOR
            //FxCop: member: GenericType`2..ctor(!0,!1)
            string dbMemberName = p.Name.Replace(" ", "");
            string[] dbMemberNameTokens = dbMemberName.Split(new char[] { ',', '.', '(', ')' },
                StringSplitOptions.RemoveEmptyEntries);
            string[] fxCopMemberNameTokens =
                memberNameWithoutNamespaces.Split(new char[] { ',', '.', '(', ')' },
                    StringSplitOptions.RemoveEmptyEntries);
            //GenericType<T  S>  ctor  T   S   :Void
            //GenericType`2      ctor  !0  !1
            int numberOfGenericParams = int.Parse(fxCopMemberNameTokens[0].Split(new char[] { '`' })[1]);
            int offset = numberOfGenericParams - 1;
            if (Math.Abs(dbMemberNameTokens.Length - fxCopMemberNameTokens.Length) != 2)
                return false;
            int lessLengt = dbMemberNameTokens.Length < fxCopMemberNameTokens.Length
                ? dbMemberNameTokens.Length
                : fxCopMemberNameTokens.Length;
            for (int i = 1 + offset; i < lessLengt; i++)
            {
                string fxCopToken = fxCopMemberNameTokens[i - offset];
                string dbToken = dbMemberNameTokens[i];
                if ((fxCopToken.Equals(dbToken) == true
                    || (fxCopToken.Equals(dbToken) == false
                        && fxCopToken.StartsWith("!") == true)) == false)
                    return false;
            }
            return true;
        }
#elif MONO_CECIL
        private bool AreTheSame(Member p, string memberNameWithoutNamespaces)
        {
            //DB: member:    AnalysisTestTarget.GenericType`2..ctor(T,S)
            //FxCop: member: GenericType`2..ctor(!0,!1)

            string dbMemberName = EliminateNamespaces(p.Name.Replace(" ", ""),
                this.Context.Namespaces.Select(ns => ns.Name).ToList(), false);
            //GenericType`2..ctor(T,S)
            string[] dbMemberNameTokens = dbMemberName.Split(new char[] { ',', '.', '(', ')' },
                StringSplitOptions.RemoveEmptyEntries);
            string[] fxCopMemberNameTokens =
                memberNameWithoutNamespaces.Split(new char[] { ',', '.', '(', ')' },
                    StringSplitOptions.RemoveEmptyEntries);

            //GenericType`2      ctor  T   S
            //GenericType`2      ctor  !0  !1
            if (Math.Abs(dbMemberNameTokens.Length - fxCopMemberNameTokens.Length) != 0)
                return false;
            bool hasSameNotation = true;
            for (int i = 1; i < fxCopMemberNameTokens.Length; i++)
            {
                string fxCopToken = fxCopMemberNameTokens[i];
                string dbToken = dbMemberNameTokens[i];
                if (fxCopToken.Equals(dbToken) == false)
                {
                    hasSameNotation = false;
                    break;
                }
            }

            if (hasSameNotation == true)
                return true;

            Dictionary<string, string> equivalence = new Dictionary<string, string>();
            Dictionary<string, int> numberOfSameTypedParams = new Dictionary<string, int>();
            for (int i = 2; i < fxCopMemberNameTokens.Length; i++)
            {
                string fxCopToken = fxCopMemberNameTokens[i];
                string dbToken = dbMemberNameTokens[i];
                if (fxCopToken.StartsWith("!") == true)
                {
                    if ((equivalence.Keys.Contains(fxCopToken) == true
                        || equivalence.Keys.Any(e => e.StartsWith(fxCopToken + ".") == true))
                        && equivalence[fxCopToken].Equals(dbToken) == false)
                        return false;
                    else if (equivalence.Keys.Contains(fxCopToken) == true
                        || equivalence.Keys.Any(e => e.StartsWith(fxCopToken + ".")) == true)//&& equivalence[fxCopToken].Equals(dbToken) == true
                    {
                        equivalence.Add(fxCopToken + "." + numberOfSameTypedParams[fxCopToken].ToString(), dbToken);
                        numberOfSameTypedParams[fxCopToken] += 1;
                    }
                    else
                    {
                        equivalence.Add(fxCopToken, dbToken);//fxCopToken == "!0", dbToken == "T" -nél nem lehet hozzáadni, mert már van ilyen
                        numberOfSameTypedParams[fxCopToken] = 1;
                    }
                    //INUnitEqualityComparer`1.AreEqual(T,T,NUnit.Framework.Constraints.Tolerance&) - member
                    //

                }
                else
                    return false;
            }

            return true;
        }
#endif

        private string ResolveSpecialCharacters(string name, bool resolveNested)
        {
            string resolvedName = name.Replace("&lt;", "<");
            resolvedName = resolvedName.Replace("&lt;", "<");
            resolvedName = resolvedName.Replace("&gt;", ">");
            resolvedName = resolvedName.Replace("&amp;", "&");
            if (resolveNested == true)
                resolvedName = resolvedName.Replace('+', '.');
            //for (int i = 0; i < 10; i++)
            //    resolvedName = resolvedName.Replace("`" + i.ToString(), "");
            // `i azt jelöli, hogy hány típusparamétere van a generikus típusnak
            return resolvedName;
        }

        private string[] ResolveNestedTypeSyntax(string typeName)
        {
            return typeName.Split(new char[] { '+' }, StringSplitOptions.RemoveEmptyEntries);
        }

        private bool TryResolveIndexer(string pureMemberName,
            out string resolvedGetterIndexer, out string resolvedSetterIndexer)
        {
            if (pureMemberName.Contains("[]") == false && pureMemberName.Contains('[') == true
                && pureMemberName.Contains(']') == true)
            {
                string indexer = pureMemberName.Replace('[', '(').Replace(']', ')');
                resolvedGetterIndexer = "get_" + indexer;
                resolvedSetterIndexer = "set_" + indexer;
                return true;
            }
            else
            {
                resolvedGetterIndexer = null;
                resolvedSetterIndexer = null;
                return false;
            }
        }

        private static List<string> sNamespaceTokens = null;

        private static string EliminateNamespaces(string memberName, List<string> namespaces, bool overwriteNsTokens)
        {
            InitNamespaceTokens(namespaces, overwriteNsTokens);

            string[] tokens = memberName.Replace(" ", "")
                .Split(new char[] { '(' }, StringSplitOptions.RemoveEmptyEntries);
            if (tokens.Length == 2)
            {
                string[] tokens1 =
                    tokens[1].Split(new char[] { ')' }, StringSplitOptions.RemoveEmptyEntries);
                #region Params
                string parameters = "";
                if (tokens1.Length > 0)
                {
                    /* a.b.t1,a.t2,c.t3 -> .a..b..t1.,.a..t2,.c..t3.
                     * a.g<a.b.t>,a.t2  -> .a..g<.a..b..t.>.,.a..t2.
                     */
                    if (tokens1[0].Contains('.') == false)
                        parameters = tokens1[0];
                    else
                    {
                        parameters =
                            "."
                            + (tokens1[0].Replace(".", "..").Replace(",", ".,.")
                                .Replace("<", "<.").Replace(">", ".>"))
                            + ".";
                        foreach (var ns in sNamespaceTokens)//this.Context.Namespaces)
                        {
                            //string[] nsTokens = ns./*Name.*/Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                            //foreach (var token in nsTokens)
                            //{
                            string toReplace = "." + ns + ".";
                            if ((parameters.Contains(toReplace + ",") == true // .a..b..t1., -> ,
                                && parameters.Contains("," + toReplace + ".") == false // ,.c..t3. -> ,
                                    && parameters.Contains("," + toReplace + ",") == true) // ,.a..t2, -> ,,
                                    == false)
                                parameters = parameters.Replace(toReplace, "");
                        }
                        /* .a..b..t1.,.a..t2,.c..t3  -> .t1.,.t2.,.t3.
                         * .a..g<.a..b..t.>.,.a..t2. -> .g<.t.>.,.t2.
                         */
                        Debug.Assert(parameters.Count(ch => ch.Equals('.'))
                            == parameters.Count(ch => ch.Equals(',')) * 2 + 2
                            + parameters.Count(ch => ch.Equals('<')) + parameters.Count(ch => ch.Equals('>')));
                        /* .t1.,.t2.,.t3. -> t1,t2,t3
                         * .g<.t.>.,.t2.  -> g<t>,t2
                         */
                        parameters = parameters.Replace(".", "");
                        Debug.Assert(parameters.Count(ch => ch.Equals('.')) == 0, "Not all namespace prefixes are elliminated!");
                    }
                }
                #endregion Params

                if (tokens[0].Count(chr => chr == '.') > 1)
                {
                    foreach (var ns in sNamespaceTokens)//this.Context.Namespaces)
                    {
                        if (tokens[0].Contains(ns))
                        {
                            var temp = tokens[0].Replace(ns + ".", "");
                            if (temp.Count(chr => chr == '.') == 1)
                            {
                                tokens[0] = temp;
                                break;
                            }
                        }
                    }
                }
                memberName = tokens[0] + "(" + parameters + ")" + (tokens1.Length > 1 ? tokens1[1] : "");
                memberName.Replace(",", ", ");
            }
            return memberName;
        }

        private static void InitNamespaceTokens(List<string> namespaces, bool overwriteNsTokens)
        {
            if (sNamespaceTokens == null || overwriteNsTokens == true)
            {
                sNamespaceTokens = new List<string>(namespaces.Count);
                foreach (var ns in namespaces)
                {
                    string[] nsTokens = ns.Split(new char[] { '.' });
                    for (int i = 0; i < nsTokens.Length; i++)
                    {
                        string newNsToken = nsTokens[i];
                        for (int j = i + 1; j < nsTokens.Length; j++)
                        {
                            newNsToken += "." + nsTokens[j];
                        }
                        if (sNamespaceTokens.Count(n => n.Equals(newNsToken)) == 0)
                            sNamespaceTokens.Add(newNsToken);
                    }
                }
                //sNamespaceTokens.Sort();

                sNamespaceTokens.Sort((s1, s2) =>
                {
                    if (s1.Length > s2.Length)
                        return 1;
                    else if (s1.Length < s2.Length)
                        return -1;
                    else
                        return 0;
                });
            }
        }

        private int ParseMessage(IEnumerable<XElement> xMessages, IEnumerable<XElement> xRules,
            int max, int count,
            Assembly assembly, Module module,
            Namespace ns = null, Model.Type type = null, Member member = null,
            bool storeModuleMessagesOnAssembly = false)
        {
            int retVal = count;
            foreach (var mess in xMessages)
            {
                Message_FxCopMessage message =
                    new Message_FxCopMessage()
                    {
                        Created = DateTime.Parse(mess.Attribute("Created").Value),
                        FixCategory = mess.Attribute("FixCategory").Value,
                        Status = mess.Attribute("Status").Value
                    };
                string checkId = mess.Attribute("CheckId").Value;
                var ruleQ = this.Context.Rule_FxCopRule.Where(f => f.Id.Equals(checkId));
                if (ruleQ.Count() == 0)
                    PostSaveRule(checkId, xRules);
                Rule_FxCopRule fxCopRule = ruleQ.First();
                Message baseMessage =
                    this.Context.Message_FxCopMessage.AddObjectSafe(entity: message, rule: fxCopRule.Rule,
                        assembly: assembly, module: module, ns: ns, type: type, member: member);
                #region Issues
                var issues = from element in mess.Elements("Issue")
                             select element;
                foreach (var i in issues)
                {
                    XAttribute file = i.Attribute("File");
                    XAttribute line = i.Attribute("Line");
                    XAttribute name = i.Attribute("Name");
                    XAttribute path = i.Attribute("Path");
                    FxCopIssue issue =
                        new FxCopIssue()
                        {
                            Certainty = int.Parse(i.Attribute("Certainty").Value),
                            File = (file == null ? null : file.Value),
                            //FxCopMessageId = message.Id,
                            Level = i.Attribute("Level").Value,
                            Line = (line == null ? null : line.Value),
                            Message_FxCopMessage = message,
                            Name = (name == null ? null : name.Value),
                            Path = (path == null ? null : path.Value),
                            Content = (string)i
                        };
                    //this.Context.FxCopIssues.AddObject(issue);
                    message.FxCopIssues.Add(issue);
                    this.Context.SaveChanges();
                }
                #endregion Issues
                #region ...
                if (assembly != null)
                {
                    baseMessage.Assembly = assembly;
                    baseMessage.AssemblyName = assembly.Name;
                    if (storeModuleMessagesOnAssembly == true)
                        assembly.Messages.Add(baseMessage);
                }
                if (module != null)
                {
                    baseMessage.ModuleAssemblyName = assembly.Name;
                    baseMessage.ModuleName = module.Name;
                }
                if (ns != null)
                {
                    baseMessage.Namespace = ns;
                    baseMessage.NamespaceName = ns.Name;
                    ns.Messages.Add(baseMessage);
                }
                if (type != null)
                {
                    baseMessage.Type = type;
                    baseMessage.TypeName = type.Name;
                    baseMessage.TypeNamespaceName = type.NamespaceName;
                    baseMessage.TypeModuleName = type.ModuleName;
                    baseMessage.TypeModuleAssemblyName = type.ModuleAssemblyName;
                    type.Messages.Add(baseMessage);
                }
                if (member != null)
                {
                    baseMessage.Member = member;
                    baseMessage.MemberName = member.Name;
                    baseMessage.MemberTypeName = member.TypeName;
                    baseMessage.MemberTypeNamespaceName = member.TypeNamespaceName;
                    baseMessage.MemberTypeModuleName = member.TypeModuleName;
                    baseMessage.MemberTypeModuleAssemblyName = member.TypeModuleAssemblyName;
                    member.Messages.Add(baseMessage);
                }
                #endregion ...
                this.Context.SaveChanges();
                retVal++;
                OnParsingProgressChanged(new ProgressChangedEventArgs((int)(((double)retVal) / ((double)max)) * 100, null));
            }
            return retVal;
        }

        private void PostSaveRule(string checkId, IEnumerable<XElement> xRules)
        {
            var rule = xRules.Where(r => r.Attribute("CheckId").Value.Equals(checkId)).First();

            Rule_FxCopRule fxCopRule =
                new Rule_FxCopRule()
                {
                    TypeName = rule.Attribute("TypeName").Value,
                    Category = rule.Attribute("Category").Value,
                    Id = rule.Attribute("CheckId").Value,
                    Name = rule.Element("Name").Value,
                    Description = rule.Element("Description").Value,
                    Url = rule.Element("Url").Value,
                    Email = rule.Element("Email").Value,
                    MessageLevelCertainty = (int)rule.Element("MessageLevel").Attribute("Certainty"),
                    MessageLevelContent = rule.Element("MessageLevel").Value,
                    FixCategories = rule.Element("FixCategories").Value,
                    FileName = rule.Element("File").Attribute("Name").Value,
                    FileVersion = rule.Element("File").Attribute("Version").Value
                };

            foreach (var res in rule.Elements("Resolution"))
            {
                if (this.Context.FxCopResolutions.Where(
                    r => r.Name.Equals(res.Attribute("Name").Value)
                        && r.FxCopRuleId.Equals(fxCopRule.Id))
                        .Count() == 0)
                {
                    FxCopResolution resolution = new FxCopResolution()
                    {
                        FxCopRuleId = fxCopRule.Id,
                        Name = res.Attribute("Name").Value,
                        Content = res.Value
                    };
                    this.Context.FxCopResolutions.AddObject(resolution);
                }
            }
            //this.Context.Rule_FxCopRule.AddObjectSafe(fxCopRule);
            if (this.Context.Rule_FxCopRule.Where(r => r.Id.Equals(fxCopRule.Id)).Count() > 0)
                this.Context.Detach(fxCopRule);
            else
            {
                this.Context.Rule_FxCopRule.AddObject(fxCopRule);
                this.Context.SaveChanges();
                Trace.TraceInformation(fxCopRule.ToString() + " post-saved to DB.");
            }
        }

        #endregion Methods

        #region Events

        public event EventHandler<ProgressChangedEventArgs> ParsingProgressChanged;
        protected virtual void OnParsingProgressChanged(ProgressChangedEventArgs e)
        {
            if (this.ParsingProgressChanged != null)
                this.ParsingProgressChanged(this, e);
        }

        protected void OnExtractionProgressChanged(int done, int max)
        {
            OnParsingProgressChanged(
                new ProgressChangedEventArgs(
                    (int)(((double)done / (double)max) * 100.0), null));
        }

        #endregion Events
    }
}
